#include "syscall.h"

/* constant number for prices */
#define TICKET_PRICE 12
#define POPCORN_PRICE 5
#define SODA_PRICE 4
#define MAX_CLERK 5
#define MAX_TICKET_TAKER 3
#define SEATS 25
#define MAX_CUSTOMER 100
#define MAX_GROUP 100

/* both for ticket clerks, concession clerks and ticket takers */
typedef enum { C_READY, C_BUSY, C_BREAK } ClerkState;

typedef enum {M_WAITING, M_STARTED, M_FINISHED, M_INIT}MovieState;

/* for allocate id */
int idMutex;
int groupSizes[MAX_GROUP];
int customerID;
int ttID;
int tcID;
int ccID;
int headCustomerID;

int outputLock;

/* for shared resources */
int incomeMutex;
int seatMutex;
int leaveMutex;
int totalIncome;
int seatRow[5];

/* number of each thread */
int numberOfCustomers;
int numberOfGroups;
int numberOfTicketClerks;
int numberOfConcessionClerks;
int numberOfTicketTakers;

/* for group commucation
make sure the group move together */
int groupLock[MAX_GROUP];
int groupCV[MAX_GROUP];
int hcCV[MAX_GROUP];
int groupMV[MAX_GROUP];
int customerPopcorn[MAX_CUSTOMER];
int customerSoda[MAX_CUSTOMER];
int customerBathroomNumber[MAX_GROUP];
int customerSeat[MAX_CUSTOMER];
int leaveCount;
int groupBathroomCV[MAX_GROUP];
int groupBathroomMV[MAX_GROUP];

/* for buy ticket */
int tcLineLock;
int tcLock[MAX_CLERK];
int tcLineCV[MAX_CLERK];
int tcCV[MAX_CLERK];
int tcTicket[MAX_CLERK];
int tcPrice[MAX_CLERK];
int tcLineCount[MAX_CLERK];
int tcIncome[MAX_CLERK];
ClerkState tcState[MAX_CLERK];
int tcBreak[MAX_CLERK];
int tcBreakCV;
int tcBreakTime[MAX_CLERK];

/* for buy food */
int ccLineLock;
int ccLock[MAX_CLERK];
int ccLineCV[MAX_CLERK];
int ccCV[MAX_CLERK];
int ccPopcorn[MAX_CLERK];
int ccSoda[MAX_CLERK];
int ccPrice[MAX_CLERK];
int ccLineCount[MAX_CLERK];
int ccIncome[MAX_CLERK];
ClerkState ccState[MAX_CLERK];
int ccBreak[MAX_CLERK];
int ccBreakCV;
int ccBreakTime[MAX_CLERK];

/* for ticket taker */
int ttLineLock;
int ttLock[MAX_TICKET_TAKER];
int ttLineCV[MAX_TICKET_TAKER];
int ttCV[MAX_TICKET_TAKER];
int ttLineCount[MAX_TICKET_TAKER];
int ttTicket[MAX_TICKET_TAKER];
int ttStop[MAX_TICKET_TAKER];
int ttProcess[MAX_TICKET_TAKER];
int ttRemainSeat;
int ttTicketTotal;
ClerkState ttState[MAX_TICKET_TAKER];
int ttBreak[MAX_TICKET_TAKER];
int ttBreakCV;
int ttBreakTime[MAX_TICKET_TAKER];

/* for playing the movie */
int customerReadyWatchingMovie;
int movieLock;
int movieCV;
int mtLock;
int mtCV;
MovieState movieState;

/* for lobby */
int lobbyCV;

/* for bathroom */
int bathroomLock;

void init() {
	int i;
	outputLock = CreateLock("Output", 6);
	idMutex = CreateLock("ID", 2);
	customerID = 0;
	headCustomerID = 0;	

	totalIncome = 0;
	leaveMutex = CreateLock("Leaving Mutex", 13);
	seatMutex = CreateLock("Seat Mutex", 10);
	leaveCount = 0;
	ttRemainSeat = SEATS;
	ttTicketTotal = 0;
	movieLock = CreateLock("Movie Lock", 10);
	movieCV = CreateCondition("Movie CV", 8);
	mtLock = CreateLock("MT Lock", 7);
	mtCV = CreateCondition("MT CV", 5);
	lobbyCV = CreateCondition("Lobby CV", 8);
	movieState = M_INIT;
	ttLineLock = CreateLock("TT Line Lock", 12);
	incomeMutex = CreateLock("Income Mutex", 12);
	tcLineLock = CreateLock("TC Line Lock", 12);
	ccLineLock = CreateLock("CC Line Lock", 12);  
	bathroomLock = CreateLock("Bathroom Lock", 13);
	tcBreakCV = CreateCondition("TC BREAK CV", 11);
	ccBreakCV = CreateCondition("CC BREAK CV", 11);
	ttBreakCV = CreateCondition("TT BREAK CV", 11);
	for(i = 0; i < 5; i++) {
		seatRow[i] = 5;
	}
	for(i = 0; i < numberOfGroups; i++) {
		hcCV[i] = CreateCondition("HC CV", 5);
		groupLock[i] = CreateLock("Group Lock", 10);
		groupCV[i] = CreateCondition("Group CV", 8);
		customerBathroomNumber[i] = 0;
		groupBathroomCV[i] = CreateCondition("Bath CV", 8);
		groupBathroomMV[i] = 0;
		groupMV[i] = 0;
	}
	for(i = 0; i < numberOfCustomers; i++) {
		customerPopcorn[i] = 0;
		customerSoda[i] = 0;
		customerSeat[i] = -1;
	}
	for(i = 0; i < numberOfTicketClerks; i++) {
		tcLock[i] = CreateLock("TC Lock", 7);
		tcLineCV[i] = CreateCondition("TC Line CV", 10);
		tcCV[i] = CreateCondition("TC CV", 5);
		tcLineCount[i] = 0;
		tcTicket[i] = 0;
		tcPrice[i] = 0;
		tcIncome[i] = 0;
		tcState[i] = C_BUSY;
		tcBreak[i] = 0;
		tcBreakTime[i] = 0;
	}
	for(i = 0; i < numberOfConcessionClerks; i++) {
		ccLock[i] = CreateLock("CC Lock", 7);
		ccLineCV[i] = CreateCondition("CC Line CV",10);
		ccCV[i] = CreateCondition("CC CV",5);
		ccLineCount[i] = 0;
		ccPopcorn[i] = 0;
		ccSoda[i] = 0;
		ccPrice[i] = 0;
		ccIncome[i] = 0;
		ccState[i] = C_BUSY;
		ccBreak[i] = 0;
		ccBreakTime[i] = 0;
	}
	for(i = 0; i < numberOfTicketTakers; i++) {
		ttLock[i] = CreateLock("TT Lock",7);
		ttLineCV[i] = CreateCondition("TT Line CV",10);
		ttCV[i] = CreateCondition("TT CV",5);
		ttLineCount[i] = 0;
		ttTicket[i] = 0;
		ttStop[i] = 0;
		ttState[i] = C_BUSY;
		ttBreak[i] = 0;
		ttBreakTime[i] = 0;
	}
}

void print(int x) {
	int num = 1;
	int t = x/10;
	int i;
	char a[11];
	while (t) {
		num++;
		t /= 10;
	}
	for(i = 1; i <= num; i++) {
		a[num-i] = (x%10) + '0';
		x /= 10;
	}
	Write(a, num, ConsoleOutput);
}

void prints(char *a) {
	int i;
	for(i = 0; a[i]; i++); Write(a,i,ConsoleOutput);
}

void newLine() {
	Write("\n", 1, ConsoleOutput);
}

void space() {
	Write(" ", 1, ConsoleOutput);
}

void Customer() {
	int i, j;
	int customerNumber;
	int sum = 0;
	int groupNumber;
	int groupSize;
	int arriveTime;
	int popcorn, soda;
	int bathroom;
	int bathroomTime;
	Acquire(idMutex);
	customerNumber = customerID++;
	for(i = 0;; i++) {
		sum += groupSizes[i]-1;
		if (sum > customerNumber) break;
	}
	groupNumber = i;
	groupSize = groupSizes[groupNumber];
	customerNumber += groupNumber+1;
	Release(idMutex);

	arriveTime = Rand(100);
	for(i = 0; i < arriveTime; i++) {
		Yield();
	}

	/* the customer arrives */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" in Group "); print(groupNumber); 
	prints(" has entered the movie theater.\n");
	Release(outputLock);
	Acquire(groupLock[groupNumber]);

	/* then wait for the head customer to buy the ticket */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	Release(outputLock);
	
	groupMV[groupNumber]++;
	if (groupMV[groupNumber] == groupSize - 1)
		Signal(hcCV[groupNumber], groupLock[groupNumber]);
	Wait(groupCV[groupNumber], groupLock[groupNumber]);
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	Release(outputLock);

	popcorn = Rand(4);
	soda = Rand(4);
  	/* tell the head if the customer wants any food */
	if (popcorn != 0) {
		customerPopcorn[customerNumber] = 1;
	}
	if (soda != 0) {
		customerSoda[customerNumber] = 1;
	}
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" wants "); print(customerPopcorn[customerNumber]);
	prints(" popcorn and "); print(customerSoda[customerNumber]);
	prints(" soda.\n");
	Release(outputLock);
	
	groupMV[groupNumber]++;
	if (groupMV[groupNumber] == groupSize - 1)
		Signal(hcCV[groupNumber], groupLock[groupNumber]);
	
	/* wait the head to buy food */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	Release(outputLock);
	
	Wait(groupCV[groupNumber], groupLock[groupNumber]);

	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	Release(outputLock);

	while (customerSeat[customerNumber] == -1) {
		/* there's no seat, go to lobby */
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is in the lobby.\n") ;
		Release(outputLock);
		
		groupMV[groupNumber]++;
		if (groupMV[groupNumber] == groupSize - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
		
		/* wait the head to buy food */
		Acquire(outputLock);
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" is waiting for the HeadCustomer.\n");
		Release(outputLock);
	
		Wait(groupCV[groupNumber], groupLock[groupNumber]);

		Acquire(outputLock);
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" has been told by the HeadCustomer to proceed.\n");
		Release(outputLock);
		
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is leaving the lobby.\n");
		Release(outputLock);
				
		/* wait the head to buy food */
		Acquire(outputLock);
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" is waiting for the HeadCustomer.\n");
		Release(outputLock);
		
		groupMV[groupNumber]++;
		if (groupMV[groupNumber] == groupSize - 1) {
			Signal(hcCV[groupNumber], groupLock[groupNumber]);			
		}
		
	
		Wait(groupCV[groupNumber], groupLock[groupNumber]);

		Acquire(outputLock);
		prints("Customer "); print(customerNumber); 
		prints(" of Group "); print(groupNumber); 
		prints(" has been told by the HeadCustomer to proceed.\n");
		Release(outputLock);
	}
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has found the following seat: row "); print(customerSeat[customerNumber]/5);
	prints(" and seat "); print(customerSeat[customerNumber]%5); prints(".\n");
	Release(outputLock);
	Release(groupLock[groupNumber]);

	/* see the movie */
	Acquire(movieLock);
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is sitting in a theater room seat.\n");
	Release(outputLock);

	customerReadyWatchingMovie++;
	Wait(movieCV, movieLock);
	customerReadyWatchingMovie--;
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is getting out of a theater room seat.\n");
	Release(outputLock);
	Release(movieLock);

	/* tell the head if the customer wants to go to the bathroom */
	Acquire(groupLock[groupNumber]);
	bathroom = Rand(4);
	if (bathroom == 0) {
		customerBathroomNumber[groupNumber]++;
	}
	Release(groupLock[groupNumber]);
	if (bathroom == 0) {
		/* go to the bathroom */
		Acquire(groupLock[groupNumber]);
		groupMV[groupNumber]++;
		if (groupMV[groupNumber] == groupSize - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
		Release(groupLock[groupNumber]);

		Acquire(bathroomLock);
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is going to the bathroom.\n");
		Release(outputLock);
		bathroomTime = Rand(5);
		for(i = 0; i < bathroomTime; i++) {
			Yield();
		}
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is leaving to the bathroom.\n");
		Release(outputLock);
		Release(bathroomLock);
		
		Acquire(groupLock[groupNumber]);
		groupBathroomMV[groupNumber]++;
		if (groupBathroomMV[groupNumber] == customerBathroomNumber[groupNumber]) {
			Signal(groupBathroomCV[groupNumber], groupLock[groupNumber]);
		}
	} else {
		Acquire(groupLock[groupNumber]);
		groupMV[groupNumber]++;
		if (groupMV[groupNumber] == groupSize - 1)
			Signal(hcCV[groupNumber], groupLock[groupNumber]);
	}

 	/* wait for every one comes out of the bathroom */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is in the lobby.\n");
	Release(outputLock);
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the HeadCustomer.\n");
	Release(outputLock);
	
	Wait(groupCV[groupNumber], groupLock[groupNumber]);
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" has been told by the HeadCustomer to proceed.\n");
	Release(outputLock);
		
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is leaving the lobby.\n");
	Release(outputLock);	
	
	Release(groupLock[groupNumber]);

	/* all set, ready to leave. the customer leaves */
	Acquire(leaveMutex);
	leaveCount++;
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has left the movie theater.\n");
	Release(outputLock);
	Release(leaveMutex);
	Exit(0);
}

void HeadCustomer() {
	int customerNumber;
	int groupNumber;
	int groupSize;
	int i, j;
	int sum = 0;
	int myTicketClerk = -1;
	int myConcessionClerk = -1;
	int myTicketTaker = -1;
	int times;
	int pay;
	int popcorn;
	int soda;
	int popcornForGroup = 0;
	int sodaForGroup = 0;
	int minLineNumber;
	int minLineCount;
	int cont;
	int allocated = 0;
	int start;
	int tempSeat;
	int seated;
	int taken;
	int bathroom;
	int arriveTime;
	int price;
	int bathroomTime;
	
	Acquire(idMutex);
	groupNumber = headCustomerID++;
	for(i = 0; i < groupNumber; i++) {
		sum += groupSizes[i];
	}
	customerNumber = sum;
	groupSize = groupSizes[groupNumber];
	Release(idMutex);

	/* assume a customer arrives theater at a ramdom time */
	arriveTime = Rand(100);
	for(i = 0; i < arriveTime; i++) {
		Yield();
	}

	/* the customer arrives */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber); 
	prints(" in Group "); print(groupNumber); 
	prints(" has entered the movie theater.\n");
	Release(outputLock);

	/* the head customer waits for every to arrive */
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the group to form.\n");
	Release(outputLock);

	Acquire(groupLock[groupNumber]);
	if (groupMV[groupNumber] != groupSize - 1) {
		Wait(hcCV[groupNumber], groupLock[groupNumber]);
	}
	groupMV[groupNumber] = 0;
	Release(groupLock[groupNumber]);

	/* buy the tickets */
	Acquire(tcLineLock);
	while (myTicketClerk == -1 || tcState[myTicketClerk] == C_BREAK) {
		if (myTicketClerk != -1) {
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" sees TicketClerk "); print(myTicketClerk);
			prints(" is on break.\n");
			Release(outputLock);
		}
		/* fine a ready tc */
		myTicketClerk = -1;
		for(i = 0; i < numberOfTicketClerks; i++) {
			if (tcState[i] == C_READY) {
				myTicketClerk = i;
				tcState[i] = C_BUSY;
				break;
			}
		}

		if (myTicketClerk == -1) {
			/* no one's ready. get in the shortest line */
			int minLineNumber;
			for(i = 0; i < numberOfTicketClerks; i++) {
				if (tcState[i] != C_BREAK) {
					/* there must be at least one tc who's not on break. */
					minLineNumber = i;
					break;
				}
			}
			minLineCount = tcLineCount[minLineNumber];
			for(i = minLineNumber+1; i < numberOfTicketClerks; i++) {
				if (tcState[i] != C_BREAK) {
					if (minLineCount > tcLineCount[i]) {
						minLineNumber = i;
						minLineCount = tcLineCount[i];
					}
				}
			}
			myTicketClerk = minLineNumber;
			tcLineCount[myTicketClerk]++;
			
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is getting in TicketClerk line "); print(myTicketClerk);
			prints(".\n");
			Release(outputLock);
			times = tcBreakTime[myTicketClerk];
			Wait(tcLineCV[myTicketClerk], tcLineLock);
			if (times != tcBreakTime[myTicketClerk]) {
				Acquire(outputLock);
				prints("Customer "); print(customerNumber);
				prints(" in Group "); print(groupNumber);
				prints(" sees TicketClerk "); print(myTicketClerk);
				prints(" is on break.\n");
				Release(outputLock);
				myTicketClerk = -1;
				continue;
			}
		}
	}
	Release(tcLineLock);
	/* interact with tc */
	Acquire(tcLock[myTicketClerk]);
	/* tell tc how many tickets */
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is walking up to TicketClerk "); print(myTicketClerk);
	prints(" to buy "); print(groupSize);
	prints(" tickets.\n");
	Release(outputLock);
	tcTicket[myTicketClerk] = groupSize;
	Signal(tcCV[myTicketClerk],tcLock[myTicketClerk]);
	Wait(tcCV[myTicketClerk],tcLock[myTicketClerk]);
	price = tcPrice[myTicketClerk];
	/* pay */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" in TicketClerk line "); print(myTicketClerk);
	prints(" is paying $"); print(price);
	prints(".00 for tickets.\n");
	Release(outputLock);
	
	Signal(tcCV[myTicketClerk], tcLock[myTicketClerk]);
	Wait(tcCV[myTicketClerk], tcLock[myTicketClerk]);
	/* leave */
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is leaving TicketClerk "); print(myTicketClerk);
	prints(".\n");
	Release(outputLock);
	Signal(tcCV[myTicketClerk], tcLock[myTicketClerk]);
	Release(tcLock[myTicketClerk]);

	/* wake up all customers then wait for them to decide if they want food */
	Acquire(groupLock[groupNumber]);
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber);
	prints(" of Group "); print(groupNumber);
	prints(" has told the group to proceed.\n");
	Release(outputLock);
	Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
	Release(groupLock[groupNumber]);      
	/* wait for every customer to tell him the food status */
	
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber); 
	prints(" of Group "); print(groupNumber); 
	prints(" is waiting for the group to form.\n");
	Release(outputLock);

	Acquire(groupLock[groupNumber]);
	if (groupMV[groupNumber] != groupSize - 1) {
		Wait(hcCV[groupNumber], groupLock[groupNumber]);
	}
	groupMV[groupNumber] = 0;
	popcorn = Rand(4);
	soda = Rand(4);
	/* tell the head if the customer wants any food */
	if (popcorn != 0) {
		customerPopcorn[customerNumber] = 1;
	}
	if (soda != 0) {
		customerSoda[customerNumber] = 1;
	}
	Release(groupLock[groupNumber]);
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" wants "); print(customerPopcorn[customerNumber]);
	prints(" popcorn and "); print(customerSoda[customerNumber]);
	prints(" soda.\n");
	Release(outputLock);
	
	Acquire(groupLock[groupNumber]);
	/* get the food request from the group members */
	for(i = 0; i < groupSize; i++) {
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" has "); print(customerPopcorn[customerNumber+i]);
		prints(" popcorn and "); print(customerSoda[customerNumber+i]);
		prints(" soda request from a group member.\n");
		Release(outputLock);
		popcornForGroup += customerPopcorn[customerNumber+i];
		sodaForGroup += customerSoda[customerNumber+i];
	}
	Release(groupLock[groupNumber]);
	/* buy food. get in line */
	Acquire(ccLineLock);
	while (myConcessionClerk == -1 || ccState[myConcessionClerk] == C_BREAK) {
		if (myConcessionClerk != -1) {
			/* cc is on break, find another line */
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" sees ConcessionClerk "); print(myConcessionClerk);
			prints(" is on break.\n");
			Release(outputLock);
		}
		myConcessionClerk = -1;
		for(i = 0; i < numberOfConcessionClerks; i++) {
			if (ccState[i] == C_READY) {
				myConcessionClerk = i;
				ccState[i] = C_BUSY;
				break;
			}
		}

		if (myConcessionClerk == -1) {
			/* no one's ready. get in the shortest line */
			int minLineNumber;
			for(i = 0; i < numberOfConcessionClerks; i++) {
				if (ccState[i] != C_BREAK) {
				 	/* there must be at least one tc who's not on break. */
					minLineNumber = i;
					break;
				}
			}
			minLineCount = ccLineCount[minLineNumber];
			for(i = minLineNumber+1; i < numberOfConcessionClerks; i++) {
				if (ccState[i] != C_BREAK) {
					if (minLineCount > ccLineCount[i]) {
						minLineNumber = i;
						minLineCount = ccLineCount[i];
					}
				}
			}
			myConcessionClerk = minLineNumber;
			ccLineCount[myConcessionClerk]++;
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is getting in ConcessionClerk line "); print(myTicketClerk);
			prints(".\n");
			Release(outputLock);
			times = ccBreakTime[myConcessionClerk];
			Wait(ccLineCV[myConcessionClerk], ccLineLock);
			if (times != ccBreakTime[myConcessionClerk]) {
				/* cc is on break and off break, line up again */
				Acquire(outputLock);
				prints("Customer "); print(customerNumber);
				prints(" in Group "); print(groupNumber);
				prints(" sees ConcessionClerk "); print(myTicketClerk);
				prints(" is on break.\n");
				Release(outputLock);				
				myConcessionClerk = -1;
				continue;
			}
		}
	}
	Release(ccLineLock);

	/* interact with cc */
	Acquire(ccLock[myConcessionClerk]);
	/* tell cc how many tickets */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is walking up to ConcessionClerk "); print(myConcessionClerk);
	prints(" to buy "); print(popcornForGroup);
	prints(" popcorn and "); print(sodaForGroup);
	prints(" soda.\n");
	Release(outputLock);
	ccPopcorn[myConcessionClerk] = popcornForGroup;
	ccSoda[myConcessionClerk] = sodaForGroup;
	Signal(ccCV[myConcessionClerk], ccLock[myConcessionClerk]);
	Wait(ccCV[myConcessionClerk], ccLock[myConcessionClerk]);
	price = ccPrice[myConcessionClerk];
	/* pay */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" in ConcessionClerk line "); print(myConcessionClerk);
	prints(" is paying "); print(price);
	prints(".00 for food.\n");
	Release(outputLock);

	Signal(ccCV[myConcessionClerk], ccLock[myConcessionClerk]);
	Wait(ccCV[myConcessionClerk], ccLock[myConcessionClerk]);
	/* leave */
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is leaving ConcessionClerk "); print(myConcessionClerk);
	prints(".\n");
	Release(outputLock);
	
	Signal(ccCV[myConcessionClerk], ccLock[myConcessionClerk]);
	Release(ccLock[myConcessionClerk]);
	
	/* give ticket */
	while (1) {
		/* break if the ticket taker let the group in the theater room */
		myTicketTaker = -1;
		Acquire(ttLineLock);
		while (myTicketTaker == -1 || ttState[myTicketTaker] == C_BREAK) {
			if (myTicketTaker != -1) {
				/* tt is on break */
				Acquire(outputLock);
				prints("Customer "); print(customerNumber);
				prints(" in Group "); print(groupNumber);
				prints(" sees TicketTaker "); print(myTicketClerk);
				prints(" is on break.\n");
				Release(outputLock);
			}
			myTicketTaker = -1;
			cont = 0;
			for(i = 0; i < numberOfTicketTakers; i++) {
				if (ttStop[i] != 0) {
					/* there's no seat, goto lobby */
					Acquire(outputLock);
					prints("Customer "); print(customerNumber);
					prints(" in Group "); print(groupNumber);
					prints(" sees TicketTaker "); print(i);
					prints(" is no longer taking tickets. Going to the lobby.\n");
					Release(outputLock);
					
					Acquire(groupLock[groupNumber]);
					Acquire(outputLock);
					prints("HeadCustomer "); print(customerNumber);
					prints(" of Group "); print(groupNumber);
					prints(" has told the group to proceed.\n");
					Release(outputLock);
					
					Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
					Release(groupLock[groupNumber]);
					
					Acquire(outputLock);
					prints("Customer "); print(customerNumber);
					prints(" in Group "); print(groupNumber);
					prints(" is in the lobby.\n");
					Release(outputLock);
					
					Wait(lobbyCV, ttLineLock);
					
					Acquire(outputLock);
					prints("HeadCustomer "); print(customerNumber);
					prints(" of Group "); print(groupNumber);
					prints("  waiting for the group to form.\n");
					Release(outputLock);
					
					/* make sure everyone in group gets into lobby */
					Acquire(groupLock[groupNumber]);
					if (groupMV[groupNumber] != groupSize - 1) {
						Wait(hcCV[groupNumber], groupLock[groupNumber]);
					}
					groupMV[groupNumber] = 0;
					Release(groupLock[groupNumber]);
					
					Acquire(groupLock[groupNumber]);
					
					Acquire(outputLock);
					prints("HeadCustomer "); print(customerNumber);
					prints(" of Group "); print(groupNumber);
					prints(" has told the group to proceed.\n");
					Release(outputLock);
					
					/* make everyone leave the lobby */
					Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
					Release(groupLock[groupNumber]);
					
					Acquire(outputLock);
					prints("Customer "); print(customerNumber);
					prints(" in Group "); print(groupNumber);
					prints(" is leaving the lobby.\n");
					Release(outputLock);
					
					Acquire(outputLock);
					prints("HeadCustomer "); print(customerNumber);
					prints(" of Group "); print(groupNumber);
					prints("  waiting for the group to form.\n");
					Release(outputLock);
					
					Acquire(groupLock[groupNumber]);
					if (groupMV[groupNumber] != groupSize - 1) {
						Wait(hcCV[groupNumber], groupLock[groupNumber]);
					}
					groupMV[groupNumber] = 0;
					Release(groupLock[groupNumber]);
					cont = 1;
					break;
				}
			}
			if (cont) continue;

			for(i = 0; i < numberOfTicketTakers; i++) {
				/* find a tt is not busy */
				if (ttState[i] == C_READY) {
					myTicketTaker = i;
					ttState[i] = C_BUSY;
					break;
				}
			}

			if (myTicketTaker == -1) {
				/* no one's ready. get in the shortest line */
				minLineNumber;
				for(i = 0; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK) {
						/* there must be at least one tc who's not on break. */
						minLineNumber = i;
						break;
					}
				}
				/* gets in shortest line */
				minLineCount = ttLineCount[minLineNumber];
				for(i = minLineNumber+1; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK) {
						if (minLineCount > ttLineCount[i]) {
							minLineNumber = i;
							minLineCount = ttLineCount[i];
						}
					}
				}
				myTicketTaker = minLineNumber;
				ttLineCount[myTicketTaker]++;
				
				Acquire(outputLock);
				prints("Customer "); print(customerNumber);
				prints(" in Group "); print(groupNumber);
				prints(" is getting in TicketTaker line "); print(myTicketTaker);
				prints(".\n");
				Release(outputLock);
				times = ttBreakTime[myTicketTaker];
				Wait(ttLineCV[myTicketTaker], ttLineLock);
				if (times != ttBreakTime[myTicketTaker]) {
					/* the ticket taker is on break and off break, line up again */
					Acquire(outputLock);
					prints("Customer "); print(customerNumber);
					prints(" in Group "); print(groupNumber);
					prints(" sees TicketTaker "); print(myTicketTaker);
					prints(" is on break.\n");
					Release(outputLock);
					myTicketTaker = -1;
					continue;
				}
			}
		}
		if (ttStop[myTicketTaker] != 0) {
			/* there's no seat, goto lobby */
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" sees TicketTaker "); print(myTicketTaker);
			prints(" is no longer taking tickets. Going to the lobby.\n");
			Release(outputLock);

			Acquire(groupLock[groupNumber]);
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints(" has told the group to proceed.\n");
			Release(outputLock);
			
			Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
			Release(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is in the lobby.\n");
			Release(outputLock);
			
			Wait(lobbyCV, ttLineLock);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints("  waiting for the group to form.\n");
			Release(outputLock);
			
			/* make sure everyone in group gets into lobby */
			Acquire(groupLock[groupNumber]);
			if (groupMV[groupNumber] != groupSize - 1) {
				Wait(hcCV[groupNumber], groupLock[groupNumber]);
			}
			groupMV[groupNumber] = 0;
			Release(groupLock[groupNumber]);
			
			Acquire(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints(" has told the group to proceed.\n");
			Release(outputLock);
			
			Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
			Release(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is leaving the lobby.\n");
			Release(outputLock);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints("  waiting for the group to form.\n");
			Release(outputLock);
			
			Acquire(groupLock[groupNumber]);
			if (groupMV[groupNumber] != groupSize - 1) {
				Wait(hcCV[groupNumber], groupLock[groupNumber]);
			}
			groupMV[groupNumber] = 0;
			Release(groupLock[groupNumber]);
			continue;
		}
		Release(ttLineLock);
		/* interact with tt */
		Acquire(ttLock[myTicketTaker]);
		ttTicket[myTicketTaker] = groupSize;
		Signal(ttCV[myTicketTaker], ttLock[myTicketTaker]);
		Wait(ttCV[myTicketTaker], ttLock[myTicketTaker]);
		Acquire(ttLineLock);
		if (ttStop[myTicketTaker] != 0 || ttProcess[myTicketTaker] == 0) {
			Release(ttLock[myTicketTaker]);
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" sees TicketTaker "); print(myTicketTaker);
			prints(" is no longer taking tickets. Going to the lobby.\n");
			Release(outputLock);

			Acquire(groupLock[groupNumber]);
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints(" has told the group to proceed.\n");
			Release(outputLock);
			
			Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
			Release(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is in the lobby.\n");
			Release(outputLock);
			
			Wait(lobbyCV, ttLineLock);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints("  waiting for the group to form.\n");
			Release(outputLock);
			
			/* make sure everyone in group gets into lobby */
			Acquire(groupLock[groupNumber]);
			if (groupMV[groupNumber] != groupSize - 1) {
				Wait(hcCV[groupNumber], groupLock[groupNumber]);
			}
			groupMV[groupNumber] = 0;
			Release(groupLock[groupNumber]);
			
			Acquire(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints(" has told the group to proceed.\n");
			Release(outputLock);
			
			Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
			Release(groupLock[groupNumber]);
			
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is leaving the lobby.\n");
			Release(outputLock);
			
			Acquire(outputLock);
			prints("HeadCustomer "); print(customerNumber);
			prints(" of Group "); print(groupNumber);
			prints("  waiting for the group to form.\n");
			Release(outputLock);
			
			Acquire(groupLock[groupNumber]);
			if (groupMV[groupNumber] != groupSize - 1) {
				Wait(hcCV[groupNumber], groupLock[groupNumber]);
			}
			groupMV[groupNumber] = 0;
			Release(groupLock[groupNumber]);
			Release(ttLineLock);
			continue;
		} else {
			Release(ttLineLock);
			Acquire(outputLock);
			prints("Customer "); print(customerNumber);
			prints(" in Group "); print(groupNumber);
			prints(" is leaving TicketTaker "); print(myTicketTaker);
			prints(".\n");
			Release(outputLock);
			Signal(ttCV[myTicketTaker], ttLock[myTicketTaker]);
			Release(ttLock[myTicketTaker]);
			break;
		}
	}

	Acquire(groupLock[groupNumber]);
	/* allocate seat here */
	Acquire(seatMutex);

	/* same row */
	for(i = 0; i < 5; i++) {
		if (groupSize <= seatRow[i]) {
			allocated = 1;
			for(j = 0; j < groupSize; j++) {
				customerSeat[customerNumber+j] = 5 - seatRow[i] + j + 5 * i;
			}
			seatRow[i] -= groupSize;
			break;
		}
	}
	if (!allocated) {
		start = 0;
		/* find a start row */
		for(i = 0; i < 5; i++) {
			tempSeat = 0;
			for(j = i; j < 5; j++) {
				if(seatRow[j] == 0) {
					break;
				} else {
					tempSeat += seatRow[j];
					if (tempSeat >= groupSize) break;
				}
			}
			if (tempSeat >= groupSize) {
				start = i;
				break;
			}
		}
		seated = 0;
		/* alloc seats */
		for(i = start; i < 5; i++) {
			taken = 0;
			for(j = 0; j < seatRow[i]; j++) {
				customerSeat[customerNumber+seated] = i*5 + 5 - seatRow[i] + j;
				seated++;
				taken++;
				if (seated == groupSize) break;
			}
			seatRow[i] -= taken;
			if (seated == groupSize) break;
		}
	}
	Release(seatMutex);
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber);
	prints(" of Group "); print(groupNumber);
	prints(" has told the group to proceed.\n");
	Release(outputLock);

	Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has found the following seat: row "); print(customerSeat[customerNumber]/5);
	prints(" and seat "); print(customerSeat[customerNumber]%5);
	prints(".\n");
	Release(outputLock);
	Release(groupLock[groupNumber]);

	/* see the movie */
	Acquire(movieLock);
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" in sitting in a theater room seat.\n");
	Release(outputLock);
	customerReadyWatchingMovie++;
	Wait(movieCV, movieLock);
	customerReadyWatchingMovie--;
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" in getting out of a theater room seat.\n");
	Release(outputLock);
	Release(movieLock);

	/* wait for the customers tell him if they are going to the bathroom */
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber);
	prints(" of Group "); print(groupNumber);
	prints("  waiting for the group to form.\n");
	Release(outputLock);
	
	Acquire(groupLock[groupNumber]);
	if (groupMV[groupNumber] != groupSize - 1) {
		Wait(hcCV[groupNumber], groupLock[groupNumber]);
	}
	groupMV[groupNumber] = 0;
	Release(groupLock[groupNumber]);

	/* decide if go to bathroom */
	bathroom = Rand(4);
	if (bathroom == 0) {
		/* go to the bathroom */
		Acquire(bathroomLock);
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is going to the bathroom.\n");
		Release(outputLock);
		bathroomTime = Rand(5);
		for(i = 0; i < bathroomTime; i++) {
			Yield();
		}
		Acquire(outputLock);
		prints("Customer "); print(customerNumber);
		prints(" in Group "); print(groupNumber);
		prints(" is leaving to the bathroom.\n");
		Release(outputLock);
		Release(bathroomLock);
	}
	
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is in the lobby.\n");
	Release(outputLock);

	Acquire(groupLock[groupNumber]);
	if (groupBathroomMV[groupNumber] != customerBathroomNumber[groupNumber]) {
		Wait(groupBathroomCV[groupNumber], groupLock[groupNumber]);
	}
	
	Acquire(outputLock);
	prints("HeadCustomer "); print(customerNumber);
	prints(" of Group "); print(groupNumber);
	prints(" has told the group to proceed.\n");
	Release(outputLock);
	Broadcast(groupCV[groupNumber], groupLock[groupNumber]);
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" is leaving the lobby.\n");
	Release(outputLock);
	Release(groupLock[groupNumber]);

	/* all set, leave */
	Acquire(leaveMutex);
	leaveCount++;
	Acquire(outputLock);
	prints("Customer "); print(customerNumber);
	prints(" in Group "); print(groupNumber);
	prints(" has left the movie theater.\n");
	Release(outputLock);
	Release(leaveMutex);
	Exit(0);
}

void TicketClerk() {
	int data;
	Acquire(idMutex);
	data = tcID++;
	Release(idMutex);
	while (1) {
		Acquire(tcLineLock);
		if (tcBreak[data] != 0) {
			/* go on a break. wake all customers */
			tcState[data] = C_BREAK;
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);
			tcBreakTime[data]++;
			tcLineCount[data] = 0;
			Broadcast(tcLineCV[data], tcLineLock);
			Wait(tcBreakCV, tcLineLock);
				tcState[data] = C_BUSY;
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" is comming off break.\n");
			Release(outputLock);
			tcBreak[data] = 0;
		}
		if (tcLineCount[data] > 0) {
			/* there's someone in line. signal a customer */
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" has a line length "); print(tcLineCount[data]);
			prints(" and is signaling a customer.\n");
			Release(outputLock);
			tcLineCount[data]--;
			tcState[data] = C_BUSY;
			Signal(tcLineCV[data], tcLineLock);
		} else {
			/* no one in line. set ready */
			tcState[data] = C_READY;
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" has no one in line. I am available for a customer.\n");
			Release(outputLock);
		}
		Acquire(tcLock[data]);
		Release(tcLineLock);
		Wait(tcCV[data],tcLock[data]);
		Acquire(tcLineLock);
		if (tcState[data] == C_BREAK) {
			Release(tcLock[data]);
			/* waken up by manager. go on a break */
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);
			tcBreakTime[data]++;
			Wait(tcBreakCV,tcLineLock);
			Acquire(outputLock);
			prints("TicketClerk "); print(data); 
			prints(" is comming off break.\n");
			Release(outputLock);
			tcState[data] = C_BUSY;
			tcBreak[data] = 0;
			Release(tcLineLock);
			continue;
		}
		Release(tcLineLock);
		/* interact with the head customer */
		tcPrice[data] = tcTicket[data] * TICKET_PRICE;
		Acquire(outputLock);
		prints("TicketClerk "); print(data); 
		prints(" has an order for "); print(tcTicket[data]);
		prints(" and the cost is $"); print(tcPrice[data]);
		prints(".00.\n");
		Release(outputLock);
		Signal(tcCV[data], tcLock[data]);
		Wait(tcCV[data], tcLock[data]);
	    /* get the money */
		Acquire(incomeMutex);
		tcIncome[data] += tcPrice[data];
		totalIncome += tcPrice[data];
		Release(incomeMutex);
		Signal(tcCV[data], tcLock[data]);
		Wait(tcCV[data], tcLock[data]);
		Release(tcLock[data]);
	}
	Exit(0);
}

void ConcessionClerk() {
	int data;
	Acquire(idMutex);
	data = ccID++;
	Release(idMutex);

	while (1) {
		Acquire(ccLineLock);
		if (ccBreak[data] != 0) {
			/* go on a break. wake all customers */
			ccState[data] = C_BREAK;
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);
			ccBreakTime[data]++;
			ccLineCount[data] = 0;
			Broadcast(ccLineCV[data], ccLineLock);
			Wait(ccBreakCV, ccLineLock);
			ccState[data] = C_BUSY;
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" is coming off break.\n");
			Release(outputLock);
			ccBreak[data] = 0;
		}
		if (ccLineCount[data] > 0) {
			/* there's someone in line. signal a customer */
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" has a line length "); print(ccLineCount[data]);
			prints(" and is signaling a customer.\n");
			Release(outputLock);
			ccLineCount[data]--;
			ccState[data] = C_BUSY;
			Signal(ccLineCV[data], ccLineLock);
		} else {
			/* no one in line. set ready */
			ccState[data] = C_READY;
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" has no one in line. I am available for a customer.\n");
			Release(outputLock);
		}
		Acquire(ccLock[data]);
		Release(ccLineLock);
		Wait(ccCV[data], ccLock[data]);
		if (ccState[data] == C_BREAK) {
			/* waken up by manager. go on a break */
			Release(ccLock[data]);
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);
			ccBreakTime[data]++;
			Wait(ccBreakCV, ccLineLock);
			Acquire(outputLock);
			prints("ConcessionClerk "); print(data); 
			prints(" is coming off break.\n");
			Release(outputLock);			
			ccState[data] = C_BUSY;
			ccBreak[data] = 0;
			Release(ccLineLock);
			continue;
		}
		/* interact with the head customer */
		ccPrice[data] = ccPopcorn[data] * POPCORN_PRICE + ccSoda[data] * SODA_PRICE;
		Acquire(outputLock);
		prints("ConcessionClerk "); print(data);
		prints(" has an order for "); print(ccPopcorn[data]);
		prints(" popcorn and "); print(ccSoda[data]); prints(" soda. The cost is $");
		print(ccPrice[data]); prints(".00.\n");
		Release(outputLock);
		Signal(ccCV[data],ccLock[data]);
		Wait(ccCV[data],ccLock[data]);
		/* get the money */
		Acquire(incomeMutex);
		ccIncome[data] += ccPrice[data];
		totalIncome += ccPrice[data];
		Release(incomeMutex);
		Signal(ccCV[data], ccLock[data]);
		Wait(ccCV[data], ccLock[data]);
		Release(ccLock[data]);
	}
	Exit(0);
}

void TicketTaker() {
	int data;
	int i;
	int seats;
	Acquire(idMutex);
	data = ttID++;
	Release(idMutex);

	while (1) {
		Acquire(ttLineLock);
		if (ttBreak[data] != 0) {
			/* go on a break. wake all customers */
			ttState[data] = C_BREAK;
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);
			ttBreakTime[data]++;
			ttLineCount[data] = 0;
			Broadcast(ttLineCV[data], ttLineLock);
			Wait(ttBreakCV, ttLineLock);
			ttState[data] = C_BUSY;
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" is coming off break.\n");
			Release(outputLock);
			ttBreak[data] = 0;
		}
		if (ttLineCount[data] > 0) {
			/* there's someone in line signal a customer */
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" has a line length "); print(ttLineCount[data]);
			prints(" and is signaling a customer.\n");
			Release(outputLock);
			ttLineCount[data]--;
			ttState[data] = C_BUSY;
			Signal(ttLineCV[data], ttLineLock);
		} else {
			/* no one in line. set ready */
			ttState[data] = C_READY;
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" has no one in line. I am available for a customer.\n");
			Release(outputLock);
			for(i = 0; i < numberOfTicketTakers; i++) {
				if (ttStop[i] != 0) {
					ttStop[data] = 1;
					break;
				}
			}
		}
		Acquire(ttLock[data]);
		Release(ttLineLock);
		Wait(ttCV[data],ttLock[data]);
		Acquire(ttLineLock);
		if (ttState[data] == C_BREAK) {
			Release(ttLock[data]);
			/* waken up by manager. go on a break */
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" is going on break.\n");
			Release(outputLock);

			ttBreakTime[data]++;
			Wait(ttBreakCV, ttLineLock);
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" is coming off break.\n");
			Release(outputLock);
			
			ttState[data] = C_BUSY;
			ttBreak[data] = 0;
			Release(ttLineLock);
			continue;
		}
		ttProcess[data] = 0;
		/* get ticket number */
		/* allocate seat */
		seats = ttTicket[data];
		
		Acquire(outputLock);
		prints("TicketTaker "); print(data); 
		prints(" has received "); print(seats);
		prints(" tickets.\n");
		Release(outputLock);
		
		Acquire(ttLineLock);
		if (ttStop[data] != 0 || seats > ttRemainSeat) {
			/* no seats left. tell everyone in his line to wait in the lobby */
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" has received "); print(seats);
			prints(" tickets.\n");
			Release(outputLock);
			
			Acquire(outputLock);
			prints("TicketTaker "); print(data);
			prints(" is not allowing the group into the theater. The number of taken tickets is ");
			print(25-ttRemainSeat);	prints(" and the group size is "); print(seats); prints(".\n");
			Release(outputLock);
			ttStop[data] = 1;
			for(i = 0; i < numberOfTicketTakers; i++) {
				if (ttState[i] == C_READY) {
					ttStop[i] = 1;
					break;
				}
			}
			Broadcast(ttLineCV[data],ttLineLock);
			/* stop selling tickets */
			ttLineCount[data] = 0;
			Acquire(outputLock);
			prints("TicketTaker "); print(data); 
			prints(" has stopped taking tickets.\n");
			Release(outputLock);
			
			Signal(ttCV[data], ttLock[data]);
			Release(ttLock[data]);
			Wait(lobbyCV, ttLineLock);
			Release(ttLineLock);
			continue;
		} else {
			/* seats is enough */
			Acquire(outputLock);
			prints("TicketTaker "); print(data);
			prints(" is allowing the group into the theater. The number of tickets taken is ");
			print(25-ttRemainSeat);	prints(".\n");
			Release(outputLock);
			
			ttProcess[data] = 1;
			ttRemainSeat -= seats;
			ttTicketTotal += seats;
			Release(ttLineLock);
		}
		Signal(ttCV[data], ttLock[data]);
		Wait(ttCV[data], ttLock[data]);
		Release(ttLock[data]);
	}

	Exit(0);
}

void MovieTechnician() {
	int movieTime;
	int i;
	Acquire(movieLock);
	movieState = M_WAITING;
	Release(movieLock);
	while (1) {
		Acquire(mtLock);
		Wait(mtCV, mtLock);
		/* told by manager to start the movie */
		movieState = M_STARTED;
		Release(mtLock);
		Acquire(outputLock);
		prints("The MovieTechnician has started the movie.\n");
		Release(outputLock);
		movieTime = Rand(100) + 200;
		/* playing the movie */
		for(i  = 0; i < movieTime; i++) {
			Yield();
		}
		Acquire(outputLock);
		prints("The MovieTechnician has ended the movie.\n");
		Release(outputLock);
		Acquire(movieLock);
		/* movie's finished. broadcast all customers */
		Acquire(outputLock);
		prints("The MovieTechnician has told all customers to leave the theater room.\n");
		Release(outputLock);
		Broadcast(movieCV, movieLock);
		movieState = M_FINISHED;
		Release(movieLock);
	}
	
	Exit(0);
}

void Manager() {
	int tc;
	int tcb;
	int i;
	int b;
	int cc;
	int ccb;
	int tt;
	int ttb;
	int start;
	int wait;
	
	while (1) {
		/* break */
		tc = 0;
		tcb = 0;
		/* tc on break */
		/* tc no one in line */
		Acquire(tcLineLock);
		for(i = 0; i < numberOfTicketClerks; i++) {
			if (tcBreak[i] == 0) tc++;
		}
		if (tc > 1) {
			/* make sure someone's working */
			for(i = 0; i < numberOfTicketClerks; i++) {
				/* no one in line */
				if (tcState[i] == C_READY) {
					int b = Rand(5);
					if (b == 0) {
						Acquire(tcLock[i]);
						tcState[i] = C_BREAK;
						tcBreak[i] = 1;
						Acquire(outputLock);
						prints("Manager has told TicketClerk "); print(i);
						prints(" to go on break.\n");
						Release(outputLock);
						Signal(tcCV[i],tcLock[i]);
						Release(tcLock[i]);
						tcb = 1;
						break;
					}
				}
			}
			if (!tcb) {
				/* tc less than 3 in line */
				for(i = 0; i < numberOfTicketClerks; i++) {
					if (tcState[i] != C_BREAK && tcLineCount[i] < 3) {
						int b = Rand(5);
						if (b == 0) {
							tcBreak[i] = 1;
							Acquire(outputLock);
							prints("Manager has told TicketClerk "); print(i);
							prints(" to go on break.\n");
							Release(outputLock);
							tcb = 1;
							break;
						}
					}
				}
			}
 		}
		Release(tcLineLock);
		/* tc off break */
		tc = 0;
		Acquire(tcLineLock);
		for(i = 0; i < numberOfTicketClerks; i++) {
			if (tcState[i] == C_BREAK) tc++;
		}
		if (tc) {
			for(i = 0; i < numberOfTicketClerks; i++) {
				if (tcLineCount[i] >= 5) {
					/* make any tc off break */
					Signal(tcBreakCV, tcLineLock);
					break;
				}
			}
		}
		Release(tcLineLock);
		
		/* cc break */
		cc = 0;
		ccb = 0;
		/* cc on break. cc no one in line */
		Acquire(ccLineLock);
		for(i = 0; i < numberOfConcessionClerks; i++) {
			if (ccBreak[i] == 0) cc++;
		}
		if (cc > 1) {
			for(i = 0; i < numberOfConcessionClerks; i++) {
				if (ccState[i] == C_READY) {
					b = Rand(5);
					if (b == 0) {
						Acquire(ccLock[i]);
						ccState[i] = C_BREAK;
						ccBreak[i] = 1;
						Acquire(outputLock);
						prints("Manager has told ConcessionClerk "); print(i);
						prints(" to go on break.\n");
						Release(outputLock);
						Signal(ccCV[i], ccLock[i]);
						Release(ccLock[i]);
						ccb = 1;
						break;
					}
				}
			}
			if (!ccb) {
				/* cc less than 3 in line */
				for(i = 0; i < numberOfConcessionClerks; i++) {
					if (ccState[i] != C_BREAK && ccLineCount[i] < 3) {
						int b = Rand(5);
						if (b == 0) {
							ccBreak[i] = 1;
							Acquire(outputLock);
							prints("Manager has told ConcessionClerk "); print(i);
							prints(" to go on break.\n");
							Release(outputLock);
							ccb = 1;
							break;
						}
					}
				}
			}
 		}
		Release(ccLineLock);
		/* cc off break */
		cc = 0;
		Acquire(ccLineLock);
		for(i = 0; i < numberOfConcessionClerks; i++) {
			if (ccState[i] == C_BREAK) cc++;
		}
		if (cc) {
			for(i = 0; i < numberOfConcessionClerks; i++) {
				if (ccLineCount[i] >= 5) {
					Signal(ccBreakCV, ccLineLock);
					break;
				}
			}
		}
		Release(ccLineLock);
		
		/* tt break */
		tt = 0;
		ttb = 0;
		/* tt on break. tt no one in line */
		Acquire(ttLineLock);
		for(i = 0; i < numberOfTicketTakers; i++) {
			if (ttBreak[i] == 0) tt++;
		}
		if (tt > 1) {
			for(i = 0; i < numberOfTicketTakers; i++) {
				if (ttState[i] == C_READY) {
					b = Rand(5);
					if (b == 0) {
						Acquire(ttLock[i]);
						ttState[i] = C_BREAK;
						ttBreak[i] = 1;
						Acquire(outputLock);
						prints("Manager has told TicketTaker "); print(i);
						prints(" to go on break.\n");
						Release(outputLock);
						Signal(ttCV[i], ttLock[i]);
						Release(ttLock[i]);
						ttb = 1;
						break;
					}
				}
			}
			if (!ttb) {
				/* tt less than 3 in line */
				for(i = 0; i < numberOfTicketTakers; i++) {
					if (ttState[i] != C_BREAK && ttLineCount[i] < 3) {
						int b = Rand(5);
						if (b == 0) {
							ttBreak[i] = 1;
							Acquire(outputLock);
							prints("Manager has told TicketTaker "); print(i);
							prints(" to go on break.\n");
							Release(outputLock);
							ttb = 1;
							break;
						}
					}
				}
			}
 		}
		Release(ttLineLock);
		/* tt off break */
		tt = 0;
		Acquire(ttLineLock);
		for(i = 0; i < numberOfTicketTakers; i++) {
			if (ttState[i] == C_BREAK) tt++;
		}
		if (tt) {
			for(i = 0; i < numberOfTicketTakers; i++) {
				if (ttLineCount[i] >= 5) {
					Signal(ttBreakCV, ttLineLock);
					break;
				}
			}
		}
		Release(ttLineLock);
		
		/* collect money. start movie*/
		Acquire(ttLineLock);
		Acquire(movieLock);
		start = 1;
		for (i = 0; i < numberOfTicketTakers; i++) {
			if (ttStop[i] == 0 && ttState[i] != C_BREAK) {				
				start = 0;
				break;
			}
		}
		if (ttTicketTotal == numberOfCustomers) {
			/* no customers */			
			start = 1;
		}
		if (customerReadyWatchingMovie + ttRemainSeat != SEATS || customerReadyWatchingMovie == 0) {			
			/* make sure all customers sit */
			start = 0;
		}
		if (movieState != M_WAITING) {
			/* movie is not ready to start */
			start = 0;
		}
		if (start != 0) {	
			/* start the movie */
			Acquire(mtLock);
			Acquire(outputLock);
			prints("Manager is telling the MovieTechnician to start the movie.\n");
			Release(outputLock);
			Signal(mtCV, mtLock);
			Release(mtLock);
		}
		Release(movieLock);
		Release(ttLineLock);
		/* finish movie */
		Acquire(ttLineLock);
		Acquire(movieLock);
		if (movieState == M_FINISHED) {
			if (customerReadyWatchingMovie == 0) {
				/* all customers leaves. make movie waiting */
								
				movieState = M_WAITING;
				/* make ticket taker start taking */
				for(i = 0; i < numberOfTicketTakers; i++) {
					ttStop[i] = 0;
				}
				/* wake up all customers in the lobby */
				ttRemainSeat = SEATS;
				Acquire(seatMutex);
				for(i = 0; i < 5; i++)
					seatRow[i] = 5;
				Release(seatMutex);
				Broadcast(lobbyCV, ttLineLock);
			}
		}
		Release(movieLock);
		Release(ttLineLock);
		/* if all customers leave */
		Acquire(leaveMutex);
		if (numberOfCustomers == leaveCount) {
			Acquire(incomeMutex);
			Acquire(outputLock);
			prints("Total money made by office $"); print(totalIncome);
			prints(".00.\n");
			Release(outputLock);
			for(i = 0; i < numberOfTicketClerks; i++) {
				Acquire(outputLock);
				prints("Manager collected $"); print(tcIncome[i]);
				prints(".00 from TicketClerk "); print(i);
				prints(".\n");
				Release(outputLock);				
			}
			for(i = 0; i < numberOfConcessionClerks; i++) {
				Acquire(outputLock);
				prints("Manager collected $"); print(ccIncome[i]);
				prints(".00 from ConcessionClerk "); print(i);
				prints(".\n");
				Release(outputLock);				
			}
			Release(incomeMutex);
			Release(leaveMutex);
			break;
		}
		Release(leaveMutex);

		/* collect money */
		Acquire(incomeMutex);
		Acquire(outputLock);
		prints("Total money made by office $"); print(totalIncome);
		prints(".00.\n");
		Release(outputLock);
		for(i = 0; i < numberOfTicketClerks; i++) {
			Acquire(outputLock);
			prints("Manager collected $"); print(tcIncome[i]);
			prints(".00 from TicketClerk "); print(i);
			prints(".\n");
			Release(outputLock);				
		}
		for(i = 0; i < numberOfConcessionClerks; i++) {
			Acquire(outputLock);
			prints("Manager collected $"); print(ccIncome[i]);
			prints(".00 from ConcessionClerk "); print(i);
			prints(".\n");
			Release(outputLock);				
		}
		Release(incomeMutex);
		Release(leaveMutex);
		/* wait time */
		wait = 50;
		for(i = 0; i < wait; i++) {
			Yield();
		}
	}
	
	Exit(0);
}

int main() {
	int i, j, k;
	numberOfCustomers = 40;
	numberOfGroups = 12;
	groupSizes[0] = 3;
	groupSizes[1] = 5;
	groupSizes[2] = 2;
	groupSizes[3] = 3;
	groupSizes[4] = 5;
	groupSizes[5] = 2;
	groupSizes[6] = 3;
	groupSizes[7] = 5;
	groupSizes[8] = 2;
	groupSizes[9] = 3;
	groupSizes[10] = 5;
	groupSizes[11] = 2;
	numberOfTicketClerks = 3;
	numberOfTicketTakers = 1;
	numberOfConcessionClerks = 2;
	init();
	for(i = 0; i < numberOfGroups; i++) {
		Fork(HeadCustomer);
		for(j = 1; j < groupSizes[i]; j++) {
			Fork(Customer);
		}
	}
	for(i = 0; i < numberOfTicketClerks; i++) {
		Fork(TicketClerk);
	}
	
	for(i = 0; i < numberOfTicketTakers; i++) {
		Fork(TicketTaker);
	}
	
	for(i = 0; i < numberOfConcessionClerks; i++) {
		Fork(ConcessionClerk);
	}
	Fork(MovieTechnician);
	Fork(Manager);
	Exit(0);
}
